package com.hjw.boot.conf;

import com.hjw.boot.realm.MyShiroRealm;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.config.ShiroConfiguration;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author hjwStart
 * @time 2022-12-19 10:38
 * @version: 1.0
 */
@Configuration
public class ShiroConfig {

    private final String casServerUrlPrefix = "http://local:8080/login";
    private final String


    /**
     * 使用工厂模式，创建并初始化ShiroFilter
     *
     * @param securityManager
     * @param casFilter
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasFilter casFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
         /*
         登录成功后要跳转的连接，不设置的时候，会默认跳转到前一步的url
         比如先在浏览器中输入了http://localhost:8080/userlist,但是现在用户却没有登录，于是会跳转到登录页面，等登录认证通过后，
         页面会再次自动跳转到http://localhost:8080/userlist页面而不是登录成功后的index页面
         建议不要设置这个字段
         */
        // shiroFilterFactoryBean.setSuccessUrl(loginSuccessUrl);
        // 设置无权限访问页面
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        /*

        添加casFilter到shiroFilter中，注意，casFilter需要放到shiroFilter的前面，
        从而保证程序在进入shiro的login登录之前就会进入单点认证
        */
        Map filters = new LinkedHashMap<>();
        filters.put("casFilter", casFilter);
        //         Shiro集成CAS后，需要实现单点登出
        // 在A上登出，在B、C...上也要登出
        // 设置logout过滤器，重定向到CAS的logout在回调到Shiro的logout
         filters.put("logout",logoutFilter());
        shiroFilterFactoryBean.setFilters(filters);
        loadShiroFilterChain(shiroFilterFactoryBean);
        return shiroFilterFactoryBean;
    }

    /**
     * 实例化SecurityManager，该类是shiro的核心类
     *
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroCasRealm());
        // <!-- 用户授权/认证信息Cache, 采用EhCache 缓存 -->
//        securityManager.setCacheManager(getEhCacheManager());
        // 指定 SubjectFactory,如果要实现cas的remember me的功能，需要用到下面这个CasSubjectFactory，并设置到securityManager的subjectFactory中
        securityManager.setSubjectFactory(new CasSubjectFactory());
        return securityManager;
    }

    /**
     * 配置缓存
     * @return
     */
//    @Bean
//    public EhCacheManager getEhCacheManager() {
//        EhCacheManager em = new EhCacheManager();
//        em.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
//        return em;
//    }

    /**
     * 配置Realm，由于我们使用的是CasRealm，所以已经集成了单点登录的功能
     *
     * @return
     */
    @Bean
    public MyShiroRealm myShiroCasRealm() {
        MyShiroRealm realm = new MyShiroRealm();
        // cas登录服务器地址前缀
        realm.setCasServerUrlPrefix(casServerUrlPrefix);
        // 客户端回调地址，登录成功后的跳转地址(自己的服务地址)
        realm.setCasService(shiroServerUrlPrefix + casFilterUrlPattern);
        // 登录成功后的默认角色，此处默认为user角色
//        realm.setDefaultRoles("user");
        return realm;
    }

    /**
     * CAS过滤器
     *
     * @return
     */
    @Bean(name = "casFilter")
    public CasFilter getCasFilter() {
        CasFilter casFilter = new CasFilter();
        casFilter.setName("casFilter");
        casFilter.setEnabled(true);
        // 认证失败后跳转的URL，也就是 Shiro 执行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer验证tiket
        casFilter.setFailureUrl(loginUrl);
        // 认证成功后跳转的URL
        casFilter.setLoginUrl(loginUrl);
        return casFilter;
    }

    @Bean
    public LogoutFilter logoutFilter() {

        LogoutFilter filter = new LogoutFilter();
        filter.setRedirectUrl(logoutUrl);
        return filter;
    }

    /**
     * 加载shiroFilter权限控制规则（从数据库读取然后配置）,角色/权限信息由MyShiroCasRealm对象提供doGetAuthorizationInfo实现获取来的
     * 生产中会将这部分规则放到数据库中
     *
     * @param shiroFilterFactoryBean
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        // 下面这些规则配置最好配置到配置文件中，注意，
        // 此处加入的filter需要保证有序，所以用的LinkedHashMap
        Map filterChainDefinitionMap = new LinkedHashMap();

        filterChainDefinitionMap.put(casFilterUrlPattern, "casFilter");

        //2.不拦截的请求
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        // 此处将logout页面设置为anon，而不是logout，因为logout被单点处理，而不需要再被shiro的logoutFilter进行拦截
//        filterChainDefinitionMap.put("/logout", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/error", "anon");
        //3.拦截的请求（从本地数据库获取或者从casserver获取(webservice,http等远程方式)，看你的角色权限配置在哪里）
        filterChainDefinitionMap.put("/user", "authc"); //需要登录

        //4.登录过的不拦截
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    }
}
